home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 3 / BBS in a box - Trilogy III.iso / Files / System7 tools / Frontier / Frontier SDK 2.1 / Toolkits / Outline Sharing Toolkit / outlinesharing.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-09  |  51.7 KB  |  2,635 lines  |  [TEXT/KAHL]

  1.  
  2. /*© Copyright 1992 UserLand Software, Inc.  All Rights Reserved.*/
  3.  
  4.  
  5. #include <iac.h>
  6. #include "outlinesharing.h"
  7.  
  8.  
  9.  
  10.  
  11. hdloutlinerecord outlinedata; /*global, points to "current" structure*/
  12.  
  13.  
  14. #define diskfontnamelength 32 /*number of bytes for a font name stored on disk*/
  15.  
  16. typedef char diskfontstring [diskfontnamelength + 1];
  17.  
  18.  
  19. #define defaultfont geneva
  20. #define defaultsize 12
  21. #define defaultstyle 0
  22.  
  23. #define infinity 32767
  24.  
  25. #define longsizeof(x) (long)sizeof(x)
  26.  
  27. #define max(x,y) ((x) > (y)? (x) : (y))
  28.  
  29. #define min(x,y) ((x) < (y)? (x) : (y))
  30.  
  31.  
  32.  
  33. typedef struct tydiskoutlineheader { /*header for packed outline*/
  34.     
  35.     short versionnumber; /*Frontier 1.0 sets this to 2*/
  36.     
  37.     long sizelinetable; /*number of bytes in the linetable section of handle*/
  38.     
  39.     long sizetext; /*number of bytes in the text portion of handle*/
  40.     
  41.     short lnumcursor; /*the line number the outline cursor is on*/
  42.     
  43.     opLineSpacing linespacing; /*indicates how many pixels to skip between lines*/
  44.     
  45.     short lineindent; /*how much to indent for each level*/
  46.     
  47.     diskfontstring fontname; /*the font used to display this outline, can be saved on disk*/
  48.     
  49.     short fontsize, fontstyle;
  50.     
  51.     short vertmin, vertmax, vertcurrent; /*scrollbar info last time this outline was in a window*/
  52.     
  53.     short horizmin, horizmax, horizcurrent;
  54.     
  55.     long timecreated, timelastsave; /*time the outline was created/last saved*/
  56.     
  57.     long ctsaves; /*the number of times this outline has been packed*/
  58.     
  59.     Boolean fltextmode: 1; /*last time it was edited was it in structure mode or edit mode?*/
  60.     
  61.     Rect windowrect; /*the size and position of window that last displayed outline*/
  62.     
  63.     char waste [32]; /*room to grow*/
  64.     } tydiskoutlineheader;
  65.  
  66.  
  67. typedef struct tylinetableitem {
  68.     
  69.     Boolean flexpanded: 1; /*was the line expanded when the outline was closed?*/
  70.     
  71.     Boolean flopenwindow: 1; /*was the linked window open when its owner was closed?*/
  72.     
  73.     Boolean reserved1bit: 1; /*reserved for future versions of Frontier*/
  74.     
  75.     Boolean reserved2bit: 1; /*reserved for future versions of Frontier*/
  76.     
  77.     Boolean reserved3bit: 1; /*reserved for future versions of Frontier*/
  78.     
  79.     Boolean flcomment: 1; /*is this line a comment?*/
  80.     
  81.     Boolean flbreakpoint: 1; /*is a breakpoint set on this line?*/
  82.     
  83.     long lenrefcon; /*number of bytes that follow -- the refcon information*/
  84.     } tylinetableitem, *ptrlinetable, **hdllinetable;
  85.     
  86.  
  87. static Handle packhandle; /*global needed for packing and unpacking*/
  88.  
  89. static long ixpackhandle; /*index into packhandle*/
  90.  
  91.  
  92.  
  93.  
  94. #define ctoutlinestack 5 /*we can remember outline contexts up to 5 levels deep*/
  95.  
  96. short topoutlinestack = 0;
  97.  
  98. hdloutlinerecord outlinestack [ctoutlinestack];
  99.  
  100.  
  101.  
  102.  
  103.  
  104. static void opFillChar (void *pfill, long ctfill, char chfill) {
  105.     
  106.     /*
  107.     do a mass memory fill -- copy ctfill chfills at pfill.
  108.     */
  109.     
  110.     register char *p = pfill;
  111.     register long ct = ctfill;
  112.     register char ch = chfill;
  113.     
  114.     while (ct--) *p++ = (char) ch; /*tight loop*/
  115.     } /*opFillChar*/
  116.     
  117.  
  118. static void opClearBytes (void *pclear, long ctclear) {
  119.     
  120.     /*
  121.     fill memory with 0's.
  122.     */
  123.     
  124.     opFillChar (pclear, ctclear, (char) 0);
  125.     } /*opClearBytes*/
  126.     
  127.  
  128. static Boolean opNewClearHandle (long ctbytes, Handle *hreturned) {
  129.     
  130.     register long ct = ctbytes;
  131.     register Handle h;
  132.     
  133.     *hreturned = h = NewHandle (ct);
  134.     
  135.     if (h == nil) 
  136.         return (false);
  137.         
  138.     opClearBytes (*h, ct);
  139.     
  140.     *hreturned = h;
  141.     
  142.     return (true);
  143.     } /*opNewClearHandle*/
  144.     
  145.     
  146. static Boolean opEqualStrings (Str255 bs1, Str255 bs2) {
  147.  
  148.     /*
  149.     return true if the two strings (pascal type, with length-byte) are
  150.     equal.  return false otherwise.
  151.     */
  152.  
  153.     register char *p1 = (char *) bs1, *p2 = (char *) bs2;
  154.     register char ct = *p1 + 1;
  155.     
  156.     while (ct--) 
  157.         
  158.         if (*p1++ != *p2++)
  159.         
  160.             return (false);
  161.         
  162.     return (true); /*loop terminated*/
  163.     } /*opEqualStrings*/
  164.     
  165.     
  166. static void opCopyString (void *source, void *dest) {
  167.  
  168.     /*
  169.     create a copy of source in dest.  copy the length byte and
  170.     all the characters in the source string.
  171.  
  172.     assume the strings are pascal strings, with the length byte in
  173.     the first character of the string.
  174.     */
  175.     
  176.     register char *s = source, *d = dest;
  177.     register short i, len;
  178.     
  179.     len = (short) s [0];
  180.     
  181.     for (i = 0; i <= len; i++) 
  182.         *d++ = *s++;
  183.     } /*opCopyString*/
  184.  
  185.  
  186. static void opCopyHeapString (StringHandle hstring, Str255 bs) {
  187.     
  188.     if (hstring == nil) {
  189.         
  190.         bs [0] = 0;
  191.         
  192.         return;
  193.         }
  194.     
  195.     HLock ((Handle) hstring);
  196.     
  197.     opCopyString (*hstring, bs);
  198.     
  199.     HUnlock ((Handle) hstring);
  200.     } /*opCopyHeapString*/
  201.     
  202.  
  203. static void opMoveLeft (void *psource, void *pdest, long length) {
  204.     
  205.     /*
  206.     do a mass memory move with the left edge leading.  good for closing
  207.     up a gap in a buffer, among other things…
  208.     */
  209.     
  210.     register char *ps, *pd;
  211.     register long ctloops;
  212.     
  213.     ctloops = length;
  214.     
  215.     if (ctloops > 0) {
  216.     
  217.         ps = psource; /*copy into a register*/
  218.     
  219.         pd = pdest; /*copy into a register*/
  220.     
  221.         while (ctloops--) *pd++ = *ps++;
  222.         }
  223.     } /*opMoveLeft*/
  224.     
  225.     
  226. static Boolean opNewFilledHandle (void *pdata, long size, Handle *hreturned) {
  227.     
  228.     register Handle h;
  229.     register long ctbytes;
  230.     
  231.     ctbytes = size; 
  232.     
  233.     h = NewHandle (ctbytes);
  234.     
  235.     if (h == nil) {
  236.         
  237.         *hreturned = nil;
  238.         
  239.         return (false);
  240.         }
  241.     
  242.     opMoveLeft (pdata, *h, ctbytes);
  243.         
  244.     *hreturned = h;
  245.     
  246.     return (true);
  247.     } /*opNewFilledHandle*/
  248.     
  249.     
  250. static Boolean opEnlargeHandle (Handle hgrow, long ctgrow, char *newdata) {
  251.     
  252.     /*
  253.     make the handle big enough to hold the new data, and move the new data in
  254.     at the end of the newly enlarged handle.
  255.     */
  256.     
  257.     register Handle h = hgrow;
  258.     register long ct = ctgrow;
  259.     register char *p = newdata;
  260.     register long origsize;
  261.     
  262.     origsize = GetHandleSize (h);
  263.     
  264.     SetHandleSize (h, origsize + ct);
  265.         
  266.     if (MemError () != noErr)
  267.         return (false);
  268.         
  269.     opMoveLeft (p, *h + origsize, ct);
  270.     
  271.     return (true);
  272.     } /*opEnlargeHandle*/ 
  273.     
  274.     
  275. static Boolean opLoadFromHandle (Handle hload, long *ixload, long ctload, char *pdata) {
  276.     
  277.     /*
  278.     copy the next ctload bytes from hload into pdata and increment the index.
  279.     
  280.     return false if there aren't enough bytes.
  281.     
  282.     start ixload at 0.
  283.     */
  284.     
  285.     register Handle h = hload;
  286.     register char *p = pdata;
  287.     register long ct = ctload;
  288.     register long ix = *ixload;
  289.     register long size;
  290.     
  291.     size = GetHandleSize (h);
  292.     
  293.     if ((ix + ct) > size) /*asked for more bytes than there are*/
  294.         return (false); 
  295.         
  296.     opMoveLeft (*h + ix, p, ct); /*copy out of the handle*/
  297.     
  298.     *ixload = ix + ct; /*increment the index into the handle*/
  299.     
  300.     return (true);
  301.     } /*opLoadFromHandle*/
  302.     
  303.  
  304. static Boolean opPushTextHandle (Str255 bs, Handle htext) {
  305.     
  306.     /*
  307.     htext is a handle created with newtexthandle.
  308.     
  309.     increase the size of the handle so we can push the text of bs at 
  310.     the end of the handle (not including length byte).
  311.     */
  312.     
  313.     return (opEnlargeHandle (htext, (long) bs [0], (char *) bs + 1));
  314.     } /*opPushTextHandle*/
  315.  
  316.  
  317. static Boolean opNewHeapString (Str255 bs, StringHandle *hstring) {
  318.  
  319.     return (opNewFilledHandle (bs, (long) bs [0] + 1, (Handle *) hstring));
  320.     } /*opNewHeapString*/
  321.     
  322.     
  323. static Boolean opPushString (void *source, void *dest) {
  324.  
  325.     /*
  326.     insert the source string at the end of the destination string.
  327.     
  328.     assume the strings are pascal strings, with the length byte in
  329.     the first character of the string.
  330.     */
  331.     
  332.     register char *s = source, *d = dest;
  333.     register short lensource = s [0];
  334.     register short lendest = d [0];
  335.     register short newlen = lensource + lendest;
  336.     
  337.     if (newlen > 255)
  338.         return (false);
  339.         
  340.     d [0] = (char) newlen;
  341.     
  342.     d += (char) lendest + 1; /*point at the position we're copying into*/
  343.     
  344.     s++; /*point at 1st char after length byte*/
  345.     
  346.     while (lensource--) 
  347.         *d++ = *s++;
  348.     
  349.     return (true);
  350.     } /*opPushString*/
  351.     
  352.     
  353. static Boolean opPushChar (char ch, Str255 bs) {
  354.     
  355.     /*
  356.     insert the character at the end of a pascal string.
  357.     */
  358.     
  359.     register short len;
  360.     
  361.     len = bs [0]; 
  362.     
  363.     if (len >= 255)
  364.         return (false);
  365.     
  366.     bs [++len] = ch;
  367.     
  368.     bs [0] = len;
  369.     
  370.     return (true);
  371.     } /*opPushChar*/
  372.     
  373.     
  374. static void opAllLower (Str255 bs) {
  375.     
  376.     register char len = bs [0];
  377.     register char *p = (char *) &bs [1];
  378.     register char ch;
  379.     
  380.     while (len--) {
  381.         
  382.         ch = *p;
  383.         
  384.         if ((ch >= 'A') && (ch <= 'Z'))
  385.             *p += 32;
  386.             
  387.         p++;
  388.         } /*while*/
  389.     } /*opAllLower*/
  390.     
  391.     
  392. static void opDiskGetFontName (short fontnum, diskfontstring fontname) {
  393.     
  394.     /*
  395.     Apple recommends that fonts be stored on disk as strings.  we return the
  396.     fontname, limited in length to 32, based on the indicated font number.
  397.     */
  398.     
  399.     Str255 bs;
  400.     
  401.     GetFontName (fontnum, bs);
  402.     
  403.     if (bs [0] > diskfontnamelength)
  404.         bs [0] = diskfontnamelength;
  405.     
  406.     opCopyString (bs, fontname);
  407.     } /*opDiskGetFontName*/
  408.  
  409.  
  410. static void opDiskGetFontNum (diskfontstring fontname, short *fontnum) {
  411.     
  412.     if (fontname [0] == 0)
  413.         *fontnum = geneva;
  414.     else
  415.         GetFNum ((StringPtr) fontname, fontnum);
  416.     } /*opDiskGetFontNum*/
  417.     
  418.     
  419. pascal void opGetHeadString (hdlheadrecord hnode, Str255 bs) {
  420.     
  421.     if (hnode == nil) { /*defensive driving*/
  422.         
  423.         bs [0] = 0;
  424.         
  425.         return;
  426.         }
  427.         
  428.     opCopyHeapString ((**hnode).hstring, bs);
  429.     } /*opGetHeadString*/
  430.  
  431.  
  432. pascal Boolean opSetHeadString (hdlheadrecord hnode, Str255 bs) {
  433.     
  434.     StringHandle hstring;
  435.     
  436.     if (!opNewHeapString (bs, &hstring))
  437.         return (false);
  438.     
  439.     DisposHandle ((Handle) (**hnode).hstring);
  440.     
  441.     (**hnode).hstring = hstring;
  442.     
  443.     return (true);
  444.     } /*opSetHeadString*/
  445.  
  446.  
  447. pascal Boolean opIsFirstInList (hdlheadrecord hnode) {
  448.     
  449.     /*
  450.     return true if the node is the first at its level.
  451.     */
  452.     
  453.     return ((**hnode).headlinkup == hnode);
  454.     } /*opIsFirstInList*/
  455.     
  456.     
  457. pascal Boolean opIsLastInList (hdlheadrecord hnode) {
  458.     
  459.     /*
  460.     return true if the node is the last at its level.
  461.     */
  462.     
  463.     return ((**hnode).headlinkdown == hnode);
  464.     } /*opIsLastInList*/
  465.     
  466.  
  467. pascal Boolean opHasSubheads (hdlheadrecord hnode) {
  468.     
  469.     /*
  470.     return true if the node has subheads.
  471.     */
  472.     
  473.     return ((**hnode).headlinkright != hnode);
  474.     } /*opHasSubheads*/
  475.     
  476.  
  477. pascal Boolean opSubheadsExpanded (hdlheadrecord hnode) {
  478.     
  479.     /*
  480.     return true if the node has subheads and they are expanded.
  481.     */
  482.     
  483.     register hdlheadrecord hkid = (**hnode).headlinkright;
  484.     
  485.     if (hkid == hnode) /*no subheads*/
  486.         return (false);
  487.         
  488.     return ((**hkid).flexpanded);
  489.     } /*opSubheadsExpanded*/
  490.     
  491.     
  492. pascal Boolean opIsSubordinateTo (h1, h2) hdlheadrecord h1, h2; {
  493.     
  494.     /*
  495.     return true if h1 is part of h2's structure.
  496.     
  497.     another way of saying the same thing: return true if h2 is in 
  498.     the path back to a summit from h1.
  499.     */
  500.     
  501.     register hdlheadrecord x = h1, y = h2;
  502.     
  503.     if (x == y) /*a node is not subordinate to itself*/
  504.         return (false);
  505.     
  506.     while (true) {
  507.         
  508.         if ((**x).headlevel == 0) /*at a summit, h1 is not subordinate to h2*/
  509.             return (false);
  510.         
  511.         x = (**x).headlinkleft; /*move towards a summit*/
  512.         
  513.         if (x == y) /*x is subordinate to y*/
  514.             return (true);
  515.         } /*while*/
  516.     } /*opIsSubordinateTo*/
  517.     
  518.     
  519. pascal Boolean opIsNestedInComment (hdlheadrecord hnode) {
  520.     
  521.     /*
  522.     return true if the node is subordinate to a comment line, or if it is
  523.     a comment line itself.
  524.     */
  525.     
  526.     register hdlheadrecord x = hnode;
  527.     
  528.     while (true) {
  529.         
  530.         if ((**x).flcomment) /*it is nested inside a comment*/
  531.             return (true);
  532.             
  533.         if ((**x).headlevel <= 0) /*can't surface any further*/
  534.             return (false);
  535.             
  536.         x = (**x).headlinkleft;
  537.         } /*while*/
  538.     } /*opIsNestedInComment*/
  539.     
  540.       
  541. pascal hdlheadrecord opGetAncestor (hdlheadrecord hnode, short level) {
  542.     
  543.     /*
  544.     surface out from hnode until you reach a node at the indicated level.
  545.     
  546.     interesting function -- it's used in implementing dragging move.
  547.     */
  548.     
  549.     register hdlheadrecord x = hnode;
  550.     
  551.     while (true) {
  552.         
  553.         register short headlevel = (**x).headlevel;
  554.         
  555.         if (headlevel <= level) /*surfaced out far enough*/
  556.             return (x);
  557.             
  558.         if (headlevel <= 0) /*internal error*/
  559.             return (nil);
  560.             
  561.         x = (**x).headlinkleft;
  562.         } /*while*/
  563.     } /*opGetAncestor*/
  564.     
  565.     
  566. pascal hdlheadrecord opGetLastSubhead (hdlheadrecord hnode) {
  567.     
  568.     /*
  569.     return a handle to the last child subordinate to the node.
  570.     
  571.     if the node has no children, return nil.
  572.     */
  573.     
  574.     register hdlheadrecord x = (**hnode).headlinkright;
  575.     register hdlheadrecord nextx;
  576.     
  577.     if (x == hnode) /*has no subheads*/
  578.         return (nil);
  579.         
  580.     while (true) {
  581.         
  582.         nextx = (**x).headlinkdown;
  583.         
  584.         if (nextx == x) /*reached the last subhead*/
  585.             return (x);
  586.             
  587.         x = nextx; /*advance to next node*/
  588.         } /*while*/
  589.     } /*opGetLastSubhead*/
  590.     
  591.       
  592. pascal hdlheadrecord opGetNthSubhead (hdlheadrecord hnode, short n) {
  593.     
  594.     /*
  595.     return the nth subhead of the indicated node, nil if he doesn't have
  596.     that many subheads.
  597.     
  598.     the number is 1-based, so the first sub is returned when n == 1.
  599.     */
  600.     
  601.     register hdlheadrecord h = hnode;
  602.     register hdlheadrecord nomad = (**h).headlinkright;
  603.     register hdlheadrecord nextnomad;
  604.     register short i;
  605.     
  606.     if (n <= 0) /*special case*/
  607.         return (h);
  608.     
  609.     if (nomad == h) /*no subs*/
  610.         return (nil);
  611.     
  612.     for (i = 1; i < n; i++) {
  613.         
  614.         nextnomad = (**nomad).headlinkdown;
  615.         
  616.         if (nextnomad == nomad) /*aren't that many subs*/
  617.             return (nil);
  618.             
  619.         nomad = nextnomad; /*advance to next sub*/
  620.         } /*for*/
  621.         
  622.     return (nomad);
  623.     } /*opGetNthSubhead*/
  624.     
  625.     
  626. pascal hdlheadrecord opGetNthSummit (short n) {
  627.     
  628.     /*
  629.     return the nth summit of the current structure. 
  630.     
  631.     if there aren't enough summits, return nil.
  632.     */
  633.     
  634.     register hdlheadrecord x = (**outlinedata).hsummit;
  635.     register short ctloops = n - 1;
  636.     register short i;
  637.     
  638.     if (outlinedata == nil) /*defensive driving*/
  639.         return (nil);
  640.     
  641.     for (i = 1; i <= ctloops; i++) {
  642.         
  643.         register hdlheadrecord nextx = (**x).headlinkdown;
  644.         
  645.         if (nextx == x) /*reached the end of the list, less than n summits*/
  646.             return (nil);
  647.             
  648.         x = nextx;
  649.         } /*for*/
  650.     
  651.     return (x);
  652.     } /*opGetNthSummit*/
  653.  
  654.  
  655. pascal hdlheadrecord opGetLastExpanded (hdlheadrecord hnode) {
  656.     
  657.     /*
  658.     return the last expanded subhead under hnode.  
  659.     
  660.     if it has no subheads or it is not expanded, we return the node itself.
  661.     */
  662.     
  663.     register hdlheadrecord h = hnode;
  664.     
  665.     while (true) {
  666.     
  667.         if (!opSubheadsExpanded (h))
  668.             return (h);
  669.             
  670.         h = opGetLastSubhead (h);
  671.         } /*while*/
  672.     } /*opGetLastExpanded*/
  673.     
  674.     
  675. pascal hdlheadrecord opGetFirstAtLevel (hdlheadrecord hnode) {
  676.     
  677.     /*
  678.     shoot from hnode up to the first guy at his level, returning with
  679.     a handle to the first guy.
  680.     */
  681.     
  682.     register hdlheadrecord h = hnode;
  683.     register hdlheadrecord next;
  684.     
  685.     if (h == nil) /*defensive driving*/
  686.         return (nil);
  687.         
  688.     while (true) {
  689.         
  690.         next = (**h).headlinkup;
  691.         
  692.         if (next == h) /*no way to go*/
  693.             return (h);
  694.             
  695.         h = next;
  696.         } /*while*/
  697.     } /*opGetFirstAtLevel*/
  698.     
  699.  
  700. pascal short opCountAtLevel (hdlheadrecord hnode) {
  701.     
  702.     /*
  703.     return the number of nodes at the same level as the node.
  704.     */
  705.     
  706.     register short ct = 1;
  707.     
  708.     hnode = opGetFirstAtLevel (hnode);
  709.     
  710.     while (opGo (&hnode, opDown)) 
  711.         ct++;
  712.         
  713.     return (ct);
  714.     } /*opCountAtLevel*/
  715.     
  716.     
  717. pascal short opGetSiblingNumber (hdlheadrecord hnode) {
  718.     
  719.     /*
  720.     if hnode is the third head at its level, return 3. 
  721.     */
  722.     
  723.     register short ct = 1;
  724.     
  725.     while (opGo (&hnode, opUp))
  726.         ct++;
  727.     
  728.     return (ct);
  729.     } /*opGetSiblingNumber*/
  730.     
  731.     
  732. pascal short opCountSummits (void) {
  733.     
  734.     /*
  735.     return the number of summits in the outline.
  736.     */
  737.     
  738.     if (outlinedata == nil)
  739.         return (0);
  740.         
  741.     return (opCountAtLevel ((**outlinedata).hsummit));
  742.     } /*opCountSummits*/
  743.     
  744.     
  745. pascal short opCountSubs (hdlheadrecord hnode) {
  746.     
  747.     /*
  748.     returns the number of first level subs underneath the indicated headrecord.
  749.     */
  750.     
  751.     register hdlheadrecord hright = (**hnode).headlinkright;
  752.     
  753.     if (hright == hnode) /*no subs*/
  754.         return (0);
  755.         
  756.     return (opCountAtLevel (hright));
  757.     } /*opCountSubs*/
  758.     
  759.     
  760. pascal Boolean opRecursivelyVisit (hdlheadrecord h, short lev, opvisitcallback visit) {
  761.  
  762.     /*
  763.     visit all the children of the headrecord in level-first order, ie first visit
  764.     the node, then visit its children and so on...
  765.     */
  766.     
  767.     register hdlheadrecord nomad;
  768.     
  769.     if (h == nil)
  770.         return (true);
  771.         
  772.     nomad = (**h).headlinkright;
  773.     
  774.     if (nomad == h) /*nothing to the right*/
  775.         return (true);
  776.         
  777.     while (true) {
  778.         
  779.         if (!(*visit) (nomad))
  780.             return (false);
  781.             
  782.         if (lev > 1)
  783.             if (!opRecursivelyVisit (nomad, lev - 1, visit))
  784.                 return (false);
  785.             
  786.         if ((**nomad).headlinkdown == nomad) /*just processed last subhead*/
  787.             return (true);
  788.             
  789.         nomad = (**nomad).headlinkdown;
  790.         } /*while*/
  791.     } /*opRecursivelyVisit*/
  792.  
  793.  
  794. pascal Boolean opRecursivelyVisitKidsFirst (hdlheadrecord h, short lev, opvisitcallback visit) {
  795.     
  796.     /*
  797.     visit all the children of the headrecord in depth-first order, ie first visit the 
  798.     children, then visit the node itself. useful for operations that delete nodes, or 
  799.     might delete nodes.
  800.     */
  801.         
  802.     register hdlheadrecord nomad, nextnomad;
  803.     
  804.     if (h == nil)
  805.         return (true);
  806.         
  807.     nomad = (**h).headlinkright;
  808.     
  809.     if (nomad == h) /*nothing to the right*/
  810.         return (true);
  811.         
  812.     while (true) {
  813.         
  814.         if (lev > 1)
  815.             if (!opRecursivelyVisitKidsFirst (nomad, lev - 1, visit))
  816.                 return (false);
  817.         
  818.         nextnomad = (**nomad).headlinkdown; /*visit may dealloc nomad*/
  819.         
  820.         if (!(*visit) (nomad))
  821.             return (false);
  822.             
  823.         if (nextnomad == nomad) /*just processed last subhead*/
  824.             return (true);
  825.             
  826.         nomad = nextnomad;
  827.         } /*while*/
  828.     } /*opRecursivelyVisitKidsFirst*/
  829.     
  830.  
  831. pascal Boolean opSiblingVisiter (hdlheadrecord hnode, Boolean flkidsfirst, opvisitcallback visit) {
  832.  
  833.     /*
  834.     visit the node and all the siblings down from it and all sub-nodes.
  835.     */
  836.     
  837.     register hdlheadrecord nomad = hnode;
  838.     register hdlheadrecord nextnomad;
  839.     
  840.     if (nomad == nil) /*defensive driving*/
  841.         return (false);
  842.     
  843.     while (true) {
  844.         
  845.         nextnomad = (**nomad).headlinkdown;
  846.         
  847.         if (flkidsfirst) {
  848.             
  849.             if (!opRecursivelyVisitKidsFirst (nomad, infinity, visit))
  850.                 return (false);
  851.                 
  852.             if (!(*visit) (nomad))
  853.                 return (false);
  854.             }
  855.             
  856.         else {
  857.             if (!(*visit) (nomad))
  858.                 return (false);
  859.             
  860.             if (!opRecursivelyVisit (nomad, infinity, visit))
  861.                 return (false);
  862.             }
  863.             
  864.         if (nextnomad == nomad) /*have visited the last sibling*/
  865.             return (true);
  866.             
  867.         if (nextnomad == nil) /*11/10/88: houtlinescrap sometimes has a nil down ptr*/
  868.             return (true);
  869.             
  870.         nomad = nextnomad; /*advance to the next sibling*/
  871.         } /*while*/
  872.     } /*opSiblingVisiter*/
  873.     
  874.     
  875. pascal Boolean opVisitOutline (opvisitcallback visit) {
  876.  
  877.     return (opSiblingVisiter ((**outlinedata).hsummit, false, visit));
  878.     } /*opVisitOutline*/
  879.  
  880.  
  881. static Boolean opResetLevelsVisit (hdlheadrecord hnode) {
  882.     
  883.     register hdlheadrecord h = hnode;
  884.     
  885.     (**h).headlevel = (**(**h).headlinkleft).headlevel + 1;
  886.     
  887.     return (true);
  888.     } /*opResetLevelsVisit*/
  889.     
  890.     
  891. pascal void opResetLevels (hdlheadrecord hnode) {
  892.     
  893.     /*
  894.     the headlevel field is dependent on the structure of the outline and
  895.     can always be calculated from the structure. it's included in the 
  896.     headrcord for efficiency and convenience.
  897.     
  898.     call this routine after you've reorganized the outline, or added a 
  899.     new headline, or deleted one. we recalc the headlevel fields for all
  900.     the lines subordinate to the node. make sure it has the correct
  901.     headlevel value before calling.
  902.     */
  903.  
  904.     opRecursivelyVisit (hnode, infinity, &opResetLevelsVisit);
  905.     } /*opResetLevels*/
  906.     
  907.     
  908. short intforcount = 0;
  909.  
  910.  
  911. static Boolean opCountVisit (hdlheadrecord hnode) {
  912.     
  913.     intforcount++; /*the simplest visit routine*/
  914.     
  915.     return (true);
  916.     } /*opCountVisit*/
  917.     
  918.     
  919. static short opCountSubheads (hdlheadrecord hnode, short level) {
  920.     
  921.     intforcount = 0;
  922.     
  923.     opRecursivelyVisit (hnode, level, &opCountVisit);
  924.     
  925.     return (intforcount);
  926.     } /*opCountSubheads*/
  927.     
  928.     
  929. pascal short opCountAllHeads (void) {
  930.     
  931.     /*
  932.     return the number of headlines linked into the current outlinerecord.
  933.     */
  934.     
  935.     hdlheadrecord nomad = (**outlinedata).hsummit;
  936.     short ct = 0;
  937.     
  938.     while (true) {
  939.         
  940.         ct += opCountSubheads (nomad, infinity) + 1;
  941.         
  942.         if (!opGo (&nomad, opDown))
  943.             return (ct);
  944.         } /*while*/
  945.     } /*opCountAllHeads*/
  946.     
  947.  
  948. pascal Boolean opSetTarget (hdloutlinerecord houtline) {
  949.     
  950.     /*
  951.     call this routine to set the global if you don't care about preserving the
  952.     current outlinerecord. patterned after the target.set verb in Frontier.
  953.     */
  954.     
  955.     outlinedata = houtline;
  956.     } /*opSetTarget*/
  957.     
  958.         
  959. pascal Boolean opPushOutline (hdloutlinerecord houtline) {
  960.     
  961.     /*
  962.     when you want to temporarily work with a different outlinerecord, call this
  963.     routine, do your stuff and then call opPopOutline.
  964.     */
  965.     
  966.     if (topoutlinestack >= ctoutlinestack) {
  967.         
  968.         DebugStr ("\poutline stack overflow!");
  969.         
  970.         return (false);
  971.         }
  972.     
  973.     outlinestack [topoutlinestack++] = outlinedata;
  974.     
  975.     outlinedata = houtline;
  976.     
  977.     return (true);
  978.     } /*opPushOutline*/
  979.         
  980.  
  981. pascal Boolean opPopOutline (void) {
  982.     
  983.     if (topoutlinestack <= 0)
  984.         return (false);
  985.     
  986.     outlinedata = outlinestack [--topoutlinestack];
  987.     
  988.     return (true);
  989.     } /*opPopOutline*/
  990.  
  991.  
  992. static hdlheadrecord opBumpFlatDown (hdlheadrecord hnode, Boolean flexpanded) {
  993.     
  994.     /*
  995.     return the node that is flatdown from the indicated node.  if the Boolean
  996.     is true, then only expanded nodes are visited.
  997.     
  998.     if hnode points to the last node, return the node itself.
  999.     */
  1000.     
  1001.     register hdlheadrecord h = hnode;
  1002.     register hdlheadrecord hcheck;
  1003.     
  1004.     hcheck = (**h).headlinkright; /*first check subhead #1*/
  1005.     
  1006.     if (hcheck != h) { /*has subheads*/
  1007.         
  1008.         if (!flexpanded) /*caller doesn't care if it's expanded*/
  1009.             return (hcheck);
  1010.         
  1011.         if ((**hcheck).flexpanded)
  1012.             return (hcheck);
  1013.         }
  1014.         
  1015.     hcheck = (**h).headlinkdown; /*check next sibling*/
  1016.     
  1017.     if (hcheck != h) { /*has subheads*/
  1018.         
  1019.         if (!flexpanded) /*caller doesn't care if it's expanded*/
  1020.             return (hcheck);
  1021.             
  1022.         if ((**hcheck).flexpanded)
  1023.             return (hcheck);
  1024.         }
  1025.     
  1026.     while (true) {
  1027.         
  1028.         if ((**h).headlinkleft == h) /*at a summit*/
  1029.             return (hnode); 
  1030.         
  1031.         h = (**h).headlinkleft; /*move out to parent*/
  1032.         
  1033.         hcheck = (**h).headlinkdown; /*check next sibling*/
  1034.         
  1035.         if (hcheck != h) { /*has subheads*/
  1036.             
  1037.             if (!flexpanded) /*caller doesn't care if it's expanded*/
  1038.                 return (hcheck);
  1039.                 
  1040.             if ((**hcheck).flexpanded)
  1041.                 return (hcheck);
  1042.             }
  1043.         } /*while*/
  1044.     } /*opBumpFlatDown*/
  1045.     
  1046.     
  1047. static hdlheadrecord opBumpFlatUp (hdlheadrecord hnode, Boolean flexpanded) {
  1048.     
  1049.     register hdlheadrecord h = hnode;
  1050.     register hdlheadrecord origh = h;
  1051.     register hdlheadrecord lasth;
  1052.     
  1053.     if ((**h).headlinkup == h) { /*no way up*/
  1054.         
  1055.         if ((**h).headlinkleft != h) /*not at summit*/
  1056.             return ((**h).headlinkleft);
  1057.             
  1058.         return (origh); /*hnode is on the summit -- no movement*/
  1059.         }
  1060.     
  1061.     h = (**h).headlinkup; /*go to previous sibling*/
  1062.     
  1063.     if (flexpanded && !(**h).flexpanded) { /*special case*/
  1064.         
  1065.         if (h != origh) /*we moved, though still collapsed*/
  1066.             return (h);
  1067.         
  1068.         return ((**h).headlinkleft); /*bump out a level -- hopefully expanded*/
  1069.         }
  1070.     
  1071.     while (true) {
  1072.         
  1073.         lasth = h;
  1074.         
  1075.         h = opBumpFlatDown (h, flexpanded);
  1076.         
  1077.         if (h == origh)
  1078.             return (lasth);
  1079.         } /*while*/
  1080.     } /*opBumpFlatUp*/
  1081.  
  1082.  
  1083. static Boolean opGoUp (hdlheadrecord *hnode) {
  1084.     
  1085.     register hdlheadrecord h = *hnode;
  1086.     register hdlheadrecord hup;
  1087.     
  1088.     if (h == nil) /*defensive driving*/
  1089.         return (false);
  1090.         
  1091.     hup = (**h).headlinkup;
  1092.     
  1093.     if (hup == h)
  1094.         return (false);
  1095.         
  1096.     *hnode = hup;
  1097.     
  1098.     return (true);
  1099.     } /*opGoUp*/
  1100.     
  1101.     
  1102. static Boolean opGoDown (hdlheadrecord *hnode) {
  1103.     
  1104.     register hdlheadrecord h = *hnode;
  1105.     register hdlheadrecord hdown;
  1106.     
  1107.     if (h == nil) /*defensive driving*/
  1108.         return (false);
  1109.         
  1110.     hdown = (**h).headlinkdown;
  1111.     
  1112.     if (hdown == h)
  1113.         return (false);
  1114.         
  1115.     *hnode = hdown;
  1116.     
  1117.     return (true);
  1118.     } /*opGoDown*/
  1119.     
  1120.     
  1121. static Boolean opGoLeft (hdlheadrecord *hnode) {
  1122.     
  1123.     register hdlheadrecord h = *hnode;
  1124.     register hdlheadrecord hleft;
  1125.     
  1126.     if (h == nil) /*defensive driving*/
  1127.         return (false);
  1128.         
  1129.     hleft = (**h).headlinkleft;
  1130.     
  1131.     if (hleft == h)
  1132.         return (false);
  1133.         
  1134.     *hnode = hleft;
  1135.     
  1136.     return (true);
  1137.     } /*opGoLeft*/
  1138.     
  1139.     
  1140. static Boolean opGoRight (hdlheadrecord *hnode) {
  1141.     
  1142.     register hdlheadrecord h = *hnode;
  1143.     register hdlheadrecord hright;
  1144.     
  1145.     if (h == nil) /*defensive driving*/
  1146.         return (false);
  1147.         
  1148.     hright = (**h).headlinkright;
  1149.     
  1150.     if (hright == h)
  1151.         return (false);
  1152.         
  1153.     *hnode = hright;
  1154.     
  1155.     return (true);
  1156.     } /*opGoRight*/
  1157.     
  1158.     
  1159. pascal Boolean opGo (hdlheadrecord *hnode, opDirection dir) {
  1160.     
  1161.     /*
  1162.     flatten out the navigation routines to emulate Frontier's "op.go" verb.
  1163.     */
  1164.  
  1165.     switch (dir) {
  1166.         
  1167.         case opUp:
  1168.             return (opGoUp (hnode));
  1169.             
  1170.         case opDown:
  1171.             return (opGoDown (hnode));
  1172.             
  1173.         case opLeft:
  1174.             return (opGoLeft (hnode));
  1175.             
  1176.         case opRight:
  1177.             return (opGoRight (hnode));
  1178.             
  1179.         case opFlatup: {
  1180.             register hdlheadrecord x = *hnode;
  1181.             
  1182.             x = opBumpFlatUp (x, false);
  1183.             
  1184.             if (x == *hnode) /*no motion*/
  1185.                 return (false);
  1186.                 
  1187.             *hnode = x;
  1188.             
  1189.             return (true);
  1190.             }
  1191.             
  1192.         case opFlatdown: {
  1193.             register hdlheadrecord x = *hnode;
  1194.             
  1195.             x = opBumpFlatDown (x, false);
  1196.             
  1197.             if (x == *hnode) /*no motion*/
  1198.                 return (false);
  1199.                 
  1200.             *hnode = x;
  1201.             
  1202.             return (true);
  1203.             }
  1204.             
  1205.         } /*switch*/
  1206.         
  1207.     return (false);
  1208.     } /*opGo*/
  1209.     
  1210.     
  1211. static hdlheadrecord opRepeatedBump (opDirection dir, short ctbumps, hdlheadrecord hstart, Boolean flexpanded) {
  1212.     
  1213.     /*
  1214.     navigate from hstart in the indicated opDirection, ctbumps times.
  1215.     
  1216.     if flexpanded is true only visit nodes that are expanded.
  1217.     
  1218.     return the resulting node.
  1219.     
  1220.     this was designed early on, and doesn't return a Boolean indicating whether
  1221.     it was able to move in the desired opDirection.
  1222.     */
  1223.     
  1224.     register hdlheadrecord nomad = hstart;
  1225.     register short ct = ctbumps;
  1226.     register Boolean fl = flexpanded;
  1227.     register hdlheadrecord lastnomad;
  1228.     register short i;
  1229.     
  1230.     if (nomad == nil) /*defensive driving*/
  1231.         return (nil);
  1232.     
  1233.     if (ct < 0) {
  1234.         
  1235.         ct = -ct;
  1236.         
  1237.         switch (dir) {
  1238.             
  1239.             case opFlatup:
  1240.                 dir = opFlatdown;
  1241.                 
  1242.                 break;
  1243.                 
  1244.             case opFlatdown:
  1245.                 dir = opFlatup;
  1246.                 
  1247.                 break;
  1248.                 
  1249.             case opLeft:
  1250.                 dir = opRight;
  1251.                 
  1252.                 break;
  1253.                 
  1254.             case opRight:
  1255.                 dir = opLeft;
  1256.                 
  1257.                 break;
  1258.                 
  1259.             case opUp:
  1260.                 dir = opDown;
  1261.                 
  1262.                 break;
  1263.                 
  1264.             case opDown:
  1265.                 dir = opUp;
  1266.                 
  1267.                 break;
  1268.             } /*switch*/
  1269.         } /*ct < 0*/
  1270.         
  1271.     switch (dir) {
  1272.         
  1273.         case opFlatup:
  1274.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1275.                 
  1276.                 lastnomad = nomad;
  1277.                 
  1278.                 nomad = opBumpFlatUp (nomad, fl);
  1279.                 
  1280.                 if (nomad == lastnomad)
  1281.                     goto L1;
  1282.                 } /*for*/    
  1283.                 
  1284.             break;
  1285.             
  1286.         case opFlatdown:
  1287.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1288.                 
  1289.                 lastnomad = nomad;
  1290.                 
  1291.                 nomad = opBumpFlatDown (nomad, fl);
  1292.                 
  1293.                 if (nomad == lastnomad)
  1294.                     goto L1;
  1295.                 } /*for*/    
  1296.                 
  1297.             break;
  1298.             
  1299.         case opLeft:
  1300.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1301.                 
  1302.                 lastnomad = nomad;
  1303.                 
  1304.                 nomad = (**nomad).headlinkleft;
  1305.                 
  1306.                 if (nomad == lastnomad)
  1307.                     goto L1;
  1308.                 } /*for*/    
  1309.                 
  1310.             break;
  1311.             
  1312.         case opRight:
  1313.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1314.                 
  1315.                 lastnomad = nomad;
  1316.                 
  1317.                 nomad = (**nomad).headlinkright;
  1318.                 
  1319.                 if (nomad == lastnomad)
  1320.                     goto L1;
  1321.                 } /*for*/    
  1322.                 
  1323.             break;
  1324.             
  1325.         case opUp:
  1326.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1327.                 
  1328.                 lastnomad = nomad;
  1329.                 
  1330.                 nomad = (**nomad).headlinkup;
  1331.                 
  1332.                 if (nomad == lastnomad)
  1333.                     goto L1;
  1334.                 } /*for*/    
  1335.                 
  1336.             break;
  1337.             
  1338.         case opDown:
  1339.             for (i = 1; i <= ct; i++) { /*replicated code for speed*/
  1340.                 
  1341.                 lastnomad = nomad;
  1342.                 
  1343.                 nomad = (**nomad).headlinkdown;
  1344.                 
  1345.                 if (nomad == lastnomad)
  1346.                     goto L1;
  1347.                 } /*for*/    
  1348.                 
  1349.             break;
  1350.             
  1351.         } /*switch*/
  1352.     
  1353.     L1:
  1354.             
  1355.     return (nomad);
  1356.     } /*opRepeatedBump*/
  1357.  
  1358.  
  1359. pascal short opSetCountExpanded (void) {
  1360.     
  1361.     /*
  1362.     when you load an outline, or do something too complex to maintain
  1363.     arithmetically, call this routine to brute force compute the number
  1364.     of expanded headlines.
  1365.     */
  1366.     
  1367.     register hdloutlinerecord ho = outlinedata;
  1368.     register hdlheadrecord nomad = (**ho).hsummit;
  1369.     register short ct = 1; /*always at least one line expanded*/
  1370.     register hdlheadrecord x;
  1371.     register opDirection dir;
  1372.     
  1373.     while (true) {
  1374.         
  1375.         x = nomad;
  1376.         
  1377.         nomad = opRepeatedBump (opFlatdown, 1, nomad, true);
  1378.         
  1379.         if (nomad == x) { /*could go no further*/
  1380.             
  1381.             (**ho).ctexpanded = ct;
  1382.             
  1383.             return (ct);
  1384.             }
  1385.             
  1386.         ct++;
  1387.         } /*while*/
  1388.     } /*opSetCountExpanded*/
  1389.     
  1390.     
  1391. static void opSetExpandedBits (hdlheadrecord hsummit, Boolean fl) {
  1392.     
  1393.     /*
  1394.     set the expanded bits of the node and all its siblings as indicated
  1395.     by the Boolean.
  1396.     */
  1397.  
  1398.     register Boolean bit = fl;
  1399.     register hdlheadrecord nomad = hsummit;
  1400.     register hdlheadrecord lastnomad;
  1401.     
  1402.     if (nomad == nil) /*defensive driving*/
  1403.         return;
  1404.     
  1405.     while (true) {
  1406.         
  1407.         (**nomad).flexpanded = bit;
  1408.         
  1409.         lastnomad = nomad;
  1410.         
  1411.         nomad = (**nomad).headlinkdown;
  1412.         
  1413.         if (nomad == lastnomad) 
  1414.             return;
  1415.         } /*while*/
  1416.     } /*opSetExpandedBits*/
  1417.  
  1418.  
  1419. static void opGetFlatLineNum (hdlheadrecord hnode, short *lnum) {
  1420.     
  1421.     /*
  1422.     get the node line number for the indicated node.  the first summit is
  1423.     node 0.  its first sub, if expanded, is node 1.
  1424.     
  1425.     we compute the number by bumping our way flatup until we can bump no more,
  1426.     counting nodes as we go.
  1427.     */
  1428.     
  1429.     register hdlheadrecord nomad = hnode;
  1430.     register hdlheadrecord lastnomad;
  1431.     register short ct = 0;
  1432.     
  1433.     while (true) {
  1434.         
  1435.         lastnomad = nomad;
  1436.         
  1437.         nomad = opBumpFlatUp (nomad, true);
  1438.         
  1439.         if (nomad == lastnomad) {
  1440.             
  1441.             *lnum = ct;
  1442.             
  1443.             return;
  1444.             }
  1445.             
  1446.         ct++;
  1447.         } /*while*/
  1448.     } /*opGetFlatLineNum*/
  1449.  
  1450.  
  1451. static Boolean opNewHeadRecord (Str255 bshead, hdlheadrecord *hnewnode) {
  1452.      
  1453.      /*
  1454.      create a new headline, without linking into any structure.
  1455.      */
  1456.      
  1457.      register hdlheadrecord h;
  1458.      StringHandle hstring;
  1459.      Handle hnew;
  1460.      
  1461.      if (!opNewClearHandle (longsizeof (tyheadrecord), &hnew))
  1462.          return (false);
  1463.          
  1464.      h = *hnewnode = (hdlheadrecord) hnew; /*copy into register*/
  1465.      
  1466.      if (!opNewHeapString (bshead, &hstring)) {
  1467.      
  1468.          DisposHandle ((Handle) h);
  1469.          
  1470.          return (false);
  1471.          }
  1472.          
  1473.      (**h).hstring = hstring;
  1474.      
  1475.      (**h).headlinkup = (**h).headlinkdown = (**h).headlinkleft = (**h).headlinkright = h;
  1476.      
  1477.      (**h).fldirty = true; /*in need of display*/
  1478.      
  1479.      return (true);
  1480.      } /*opNewHeadRecord*/
  1481.  
  1482.  
  1483. static Boolean opNewStructure (Str255 bssummit, hdlheadrecord *hsummit) {
  1484.  
  1485.     /*
  1486.     create a new summit for the current outline record.
  1487.     */
  1488.     
  1489.     register hdlheadrecord hnode;
  1490.     StringHandle hstring;
  1491.     register hdlheadrecord holdsummit;
  1492.     Handle hnew;
  1493.     
  1494.     if (!opNewClearHandle (longsizeof (tyheadrecord), &hnew))
  1495.         return (false);
  1496.     
  1497.     hnode = (hdlheadrecord) hnew; /*copy into register*/
  1498.     
  1499.     if (!opNewHeapString (bssummit, &hstring)) {
  1500.     
  1501.         DisposHandle ((Handle) hnode);
  1502.         
  1503.         return (false);
  1504.         }
  1505.         
  1506.     (**hnode).hstring = hstring;
  1507.     
  1508.     (**hnode).headlinkleft = (**hnode).headlinkright = hnode;
  1509.     
  1510.     (**hnode).headlinkup = (**hnode).headlinkdown = hnode;
  1511.     
  1512.     (**hnode).fldirty = true;
  1513.     
  1514.     (**hnode).flexpanded = true;
  1515.     
  1516.     *hsummit = hnode;
  1517.     
  1518.     return (true);
  1519.     } /*opNewStructure*/
  1520.  
  1521.  
  1522. static Boolean opReleaseHeadline (hdlheadrecord hnode) {
  1523.     
  1524.     register hdlheadrecord h = hnode;
  1525.     Handle hrefcon = (Handle) (**h).headrecordrefcon;
  1526.     
  1527.     if (hrefcon != nil) /*node has a refcon handle attached*/    
  1528.         DisposHandle (hrefcon); 
  1529.     
  1530.     DisposHandle ((Handle) (**h).hstring); /*get rid of the head string*/
  1531.     
  1532.     DisposHandle ((Handle) h); 
  1533.     
  1534.     return (true);
  1535.     } /*opReleaseHeadline*/
  1536.     
  1537.     
  1538. static Boolean opSetSummit (hdloutlinerecord houtline, hdlheadrecord hnode) {
  1539.     
  1540.     /*
  1541.     establish hnode as the summit of the indicated outline.  dispose of 
  1542.     the existing summit, if one exists.  also, make sure that the levels 
  1543.     of the outline are set correctly, starting at zero.
  1544.     */
  1545.     
  1546.     register hdloutlinerecord ho = houtline;
  1547.     register hdlheadrecord h = hnode;
  1548.     
  1549.     if ((**h).headlevel != 0) {
  1550.         
  1551.         (**h).headlevel = 0;
  1552.         
  1553.         opResetLevels (h);
  1554.         }
  1555.     
  1556.     if ((**ho).hsummit != nil)
  1557.         opDisposeStructure ((**ho).hsummit);
  1558.     
  1559.     (**ho).hsummit = (**ho).hbarcursor = (**ho).hline1 = h;
  1560.     
  1561.     return (true);
  1562.     } /*opSetSummit*/
  1563.  
  1564.  
  1565. static Boolean opNewSummit (void) {
  1566.     
  1567.     /*
  1568.     create a new, blank summit for the current outline.  link it into the
  1569.     structure accordingly.
  1570.     */
  1571.     
  1572.     register hdloutlinerecord ho = outlinedata;
  1573.     hdlheadrecord hnewsummit;
  1574.     
  1575.     if (!opNewStructure ("\p", &hnewsummit))
  1576.         return (false);
  1577.     
  1578.     return (opSetSummit (ho, hnewsummit));
  1579.     } /*opNewSummit*/
  1580.  
  1581.  
  1582. pascal Boolean opNewOutlineRecord (hdloutlinerecord *houtline) {
  1583.     
  1584.     /*
  1585.     create a new outline record, returned in houtline.  we assume nothing
  1586.     about outlinewindowinfo or outlinewindow, and we preserve outlinedata.
  1587.     
  1588.     the rectangles and displayinfo are all zero after we're called.
  1589.     */
  1590.     
  1591.     register hdloutlinerecord ho;
  1592.     register Boolean fl; 
  1593.     unsigned long time;
  1594.     
  1595.     if (!opNewClearHandle (longsizeof (tyoutlinerecord), (Handle *) houtline))
  1596.         return (false);
  1597.         
  1598.     ho = *houtline; /*copy into register*/
  1599.     
  1600.     opPushOutline (ho);
  1601.     
  1602.     fl = opNewSummit ();
  1603.     
  1604.     opPopOutline ();
  1605.         
  1606.     if (!fl) {    
  1607.     
  1608.         DisposHandle ((Handle) ho);
  1609.     
  1610.         return (false);
  1611.         }
  1612.         
  1613.     (**ho).ctexpanded = 1; /*the summit line is expanded*/
  1614.     
  1615.     (**ho).lineindent = 15;
  1616.     
  1617.     (**ho).linespacing = opOneandalittlespaced;
  1618.     
  1619.     GetDateTime (&time);
  1620.     
  1621.     (**ho).timecreated = time;
  1622.     
  1623.     return (true);
  1624.     } /*opNewOutlineRecord*/
  1625.     
  1626.     
  1627. pascal void opDisposeStructure (hdlheadrecord hnode) {
  1628.     
  1629.     /*
  1630.     dispose of the structure pointed to by hnode, including all his
  1631.     submaterial and all of his siblings' submaterial.
  1632.     
  1633.     be sure to unlink the node first, if you don't want all his siblings
  1634.     to be disposed of too...
  1635.     
  1636.     if fldisk, then all disk storage used by the structure is dealloc'd
  1637.     too.
  1638.     */
  1639.     
  1640.     opSiblingVisiter (hnode, true, &opReleaseHeadline);
  1641.     } /*opDisposeStructure*/
  1642.     
  1643.     
  1644. pascal void opDisposeOutlineRecord (hdloutlinerecord houtline) {
  1645.     
  1646.     register hdloutlinerecord ho = houtline;
  1647.     register Handle hrefcon;
  1648.     
  1649.     if (ho == nil) /*defensive driving*/
  1650.         return;
  1651.     
  1652.     opPushOutline (ho); /*set the current outline to this one*/
  1653.     
  1654.     opDisposeStructure ((**ho).hsummit);
  1655.     
  1656.     opPopOutline (); /*finished popping all hoists*/
  1657.     
  1658.     hrefcon = (Handle) (**ho).outlinerefcon;
  1659.     
  1660.     if (hrefcon != nil)
  1661.         DisposHandle (hrefcon);
  1662.         
  1663.     DisposHandle ((Handle) ho);
  1664.     } /*opDisposeOutlineRecord*/
  1665.     
  1666.  
  1667. static Boolean outTableVisit (hdlheadrecord hnode) {
  1668.     
  1669.     register hdlheadrecord hn = hnode;
  1670.     register Handle hrefcon = (**hn).headrecordrefcon;
  1671.     register long lenrefcon;
  1672.     tylinetableitem item;
  1673.     
  1674.     opClearBytes (&item, longsizeof (item)); /*clear all unused bits*/
  1675.     
  1676.     item.flexpanded = (**hn).flexpanded;
  1677.     
  1678.     item.flcomment = (**hn).flcomment;
  1679.     
  1680.     item.flbreakpoint = (**hn).flbreakpoint;
  1681.     
  1682.     item.lenrefcon = lenrefcon = GetHandleSize (hrefcon); /*0 if it's nil*/
  1683.     
  1684.     (**hnode).tmpbit = false;
  1685.     
  1686.     if (!opEnlargeHandle (packhandle, longsizeof (tylinetableitem), (char *) &item)) {
  1687.     
  1688.         DisposHandle (packhandle);
  1689.         
  1690.         return (false);
  1691.         }
  1692.     
  1693.     if (lenrefcon > 0) { /*something stored in the refcon field*/
  1694.     
  1695.         HLock (hrefcon);
  1696.         
  1697.         if (!opEnlargeHandle (packhandle, item.lenrefcon, *hrefcon)) {
  1698.             
  1699.             HUnlock (hrefcon);
  1700.             
  1701.             DisposHandle (packhandle);
  1702.             
  1703.             return (false);
  1704.             }
  1705.             
  1706.         HUnlock (hrefcon);
  1707.         }
  1708.         
  1709.     return (true);
  1710.     } /*outTableVisit*/
  1711.  
  1712.  
  1713. static Boolean outlineToTable (hdlheadrecord hnode, Handle h, long *ctbytes) {
  1714.     
  1715.     /*
  1716.     traverse the current outline, producing a table of information 
  1717.     with one element for each line in the outline.  append that table
  1718.     to the end of the indicated handle and return the number of bytes
  1719.     added to the handle.
  1720.     
  1721.     return false if there was a memory error.
  1722.     */
  1723.     
  1724.     register long origsize;
  1725.     
  1726.     *ctbytes = 0;
  1727.     
  1728.     origsize = GetHandleSize (h);
  1729.     
  1730.     packhandle = h;
  1731.     
  1732.     if (!opSiblingVisiter (hnode, false, &outTableVisit))
  1733.         return (false);
  1734.         
  1735.     *ctbytes = GetHandleSize (h) - origsize;
  1736.     
  1737.     return (true);
  1738.     } /*outlineToTable*/
  1739.  
  1740.  
  1741. static Boolean outTextVisit (hdlheadrecord hnode) {
  1742.     
  1743.     register short level = (**hnode).headlevel;
  1744.     Str255 bs;
  1745.     
  1746.     bs [0] = level;
  1747.     
  1748.     opFillChar (&bs [1], (long) level, (char) 9);
  1749.     
  1750.     opPushString (*(**hnode).hstring, bs);
  1751.     
  1752.     opPushChar ((char) 13, bs);
  1753.     
  1754.     if (!opPushTextHandle (bs, packhandle)) {
  1755.     
  1756.         DisposHandle (packhandle);
  1757.         
  1758.         return (false);
  1759.         }
  1760.     
  1761.     return (true);
  1762.     } /*outTextVisit*/
  1763.     
  1764.  
  1765. static Boolean outlineToText (hdlheadrecord hnode, Handle htext, long *ctbytes) {
  1766.     
  1767.     /*
  1768.     convert the outline into a block of tab-indented text, each
  1769.     line ended by a carriage return.  suitable for saving to disk
  1770.     because we convert the whole outline, all level-0 heads in
  1771.     the current outline.
  1772.     
  1773.     push the resulting text at the end of the indicated handle and
  1774.     return in ctbytes the number of bytes added to the handle.
  1775.     
  1776.     return false if there was a memory allocation error.
  1777.     */
  1778.     
  1779.     register Handle h = htext;
  1780.     register long origbytes;
  1781.     Str255 bs;
  1782.     
  1783.     origbytes = GetHandleSize (h);
  1784.     
  1785.     *ctbytes = 0;
  1786.     
  1787.     packhandle = h;
  1788.     
  1789.     if (!opSiblingVisiter (hnode, false, &outTextVisit))
  1790.         return (false);
  1791.     
  1792.     *ctbytes = GetHandleSize (h) - origbytes;
  1793.     
  1794.     return (true);
  1795.     } /*outlineToText*/
  1796.  
  1797.  
  1798. pascal Boolean opPack (Handle *hpackedoutline) {
  1799.     
  1800.     /*
  1801.     create a packed, contiguous version of the current outline record.
  1802.     
  1803.     DW 3/25/90: if hpackedoutline comes in non-nil, we just append our
  1804.     stuff to the end of the handle, we don't allocate a new one.
  1805.     
  1806.     dmb 10/16/90: don't dispose of handle if we didn't allocate it
  1807.     */
  1808.     
  1809.     register hdloutlinerecord ho = outlinedata;
  1810.     register hdlheadrecord hsummit;
  1811.     register Handle h;
  1812.     register short ixheader;
  1813.     tydiskoutlineheader header;
  1814.     Str255 bs;
  1815.     Boolean flallocated = false;
  1816.     Boolean flerror = false;
  1817.     
  1818.     h = *hpackedoutline; /*copy into register*/
  1819.     
  1820.     opClearBytes (&header, longsizeof (header)); /*assure all bits set to 0*/
  1821.     
  1822.     if (h == nil) { /*the normal case, allocate a new handle*/
  1823.     
  1824.         if (!opNewClearHandle (longsizeof (header), hpackedoutline))
  1825.             return (false);
  1826.         
  1827.         flallocated = true;
  1828.         
  1829.         h = *hpackedoutline;
  1830.         
  1831.         ixheader = 0;
  1832.         }
  1833.     else {
  1834.         ixheader = GetHandleSize (h); /*where we move the header in*/
  1835.         
  1836.         if (!opEnlargeHandle (h, longsizeof (header), (char *) &header))
  1837.             return (false);
  1838.         }
  1839.     
  1840.     header.versionnumber = 2;
  1841.     
  1842.     opGetFlatLineNum ((**ho).hbarcursor, &header.lnumcursor);
  1843.  
  1844.     header.linespacing = (**ho).linespacing;
  1845.     
  1846.     header.lineindent = (**ho).lineindent;
  1847.     
  1848.     header.vertmin = (**ho).vertmin;
  1849.     
  1850.     header.vertmax = (**ho).vertmax;
  1851.     
  1852.     header.vertcurrent = (**ho).vertcurrent;
  1853.     
  1854.     header.horizmin = (**ho).horizmin;
  1855.     
  1856.     header.horizmax = (**ho).horizmax;
  1857.     
  1858.     header.horizcurrent = (**ho).horizcurrent;
  1859.     
  1860.     header.timecreated = (**ho).timecreated;
  1861.     
  1862.     GetDateTime ((unsigned long *) &header.timelastsave); /*stamp it*/
  1863.     
  1864.     header.ctsaves = ++(**ho).ctsaves;
  1865.     
  1866.     opDiskGetFontName ((**ho).fontnum, header.fontname);
  1867.     
  1868.     header.fontsize = (**ho).fontsize;
  1869.     
  1870.     header.fontstyle = (**ho).fontstyle;
  1871.     
  1872.     header.windowrect = (**ho).windowrect;
  1873.     
  1874.     hsummit = (**ho).hsummit; /*copy into register*/
  1875.     
  1876.     if (!outlineToText (hsummit, h, &header.sizetext)) {
  1877.         
  1878.         flerror = true;
  1879.         
  1880.         goto exit;
  1881.         }
  1882.         
  1883.     if (!outlineToTable (hsummit, h, &header.sizelinetable)) {
  1884.     
  1885.         flerror = true;
  1886.         
  1887.         goto exit;
  1888.         }
  1889.     
  1890.     /*move the header into handle*/ {
  1891.         
  1892.         register char *p;
  1893.         
  1894.         HLock (h); /*about to do a moveleft*/
  1895.         
  1896.         p = *h;
  1897.         
  1898.         p += ixheader;
  1899.         
  1900.         opMoveLeft (&header, p, longsizeof (header));
  1901.         
  1902.         HUnlock (h);
  1903.         }
  1904.     
  1905.     exit:
  1906.     
  1907.     if (flerror) {
  1908.         
  1909.         if (flallocated)
  1910.             DisposHandle (h);
  1911.         
  1912.         return (false);
  1913.         }
  1914.     
  1915.     return (true);
  1916.     } /*opPack*/
  1917.  
  1918.  
  1919. static long ixtable; /*must be global for recursion*/
  1920.     
  1921.  
  1922. static Boolean inTableVisit (hdlheadrecord hnode) {
  1923.     
  1924.     register hdlheadrecord hn = hnode;
  1925.     register char *p;
  1926.     register long lenrefcon;
  1927.     Handle hrefcon;
  1928.     tylinetableitem item;
  1929.     
  1930.     p = *packhandle + ixtable + ixpackhandle;
  1931.     
  1932.     ixpackhandle += longsizeof (tylinetableitem);
  1933.  
  1934.     opMoveLeft (p, &item, longsizeof (tylinetableitem));
  1935.     
  1936.     (**hn).flexpanded = item.flexpanded;
  1937.     
  1938.     (**hn).flcomment = item.flcomment;
  1939.     
  1940.     (**hn).flbreakpoint = item.flbreakpoint;
  1941.     
  1942.     lenrefcon = item.lenrefcon; /*copy into register*/
  1943.     
  1944.     if (lenrefcon > 0) { /*link in a refcon node*/
  1945.     
  1946.         if (!opNewClearHandle (lenrefcon, &hrefcon))
  1947.             return (false);
  1948.             
  1949.         p = *packhandle + ixtable + ixpackhandle;
  1950.         
  1951.         ixpackhandle += lenrefcon;
  1952.         
  1953.         opMoveLeft (p, *hrefcon, lenrefcon);
  1954.         
  1955.         (**hn).headrecordrefcon = hrefcon;
  1956.         }
  1957.     
  1958.     return (true);
  1959.     } /*inTableVisit*/
  1960.     
  1961.     
  1962. static Boolean tableToOutline (Handle htable, long ixstart, hdlheadrecord hsummit) { 
  1963.  
  1964.     /*
  1965.     apply values in table to hsummit outline
  1966.     */
  1967.     
  1968.     packhandle = htable;
  1969.     
  1970.     ixtable = ixstart;
  1971.     
  1972.     ixpackhandle = 0; /*pointing to first item in the table*/
  1973.     
  1974.     return (opSiblingVisiter (hsummit, false, &inTableVisit));
  1975.     } /*tableToOutline*/
  1976.     
  1977.  
  1978. static short spacesforlevel;
  1979.  
  1980. static short firstindent;
  1981.  
  1982. static Boolean fltabindent;
  1983.  
  1984.  
  1985. static clearIndentValues (Boolean flmusttabindent) {
  1986.     
  1987.     spacesforlevel = 0;
  1988.     
  1989.     firstindent = -1;
  1990.     
  1991.     fltabindent = flmusttabindent;
  1992.     } /*clearIndentValues*/
  1993.  
  1994.  
  1995. static long divRound (register long n, register long d) {
  1996.     
  1997.     /*
  1998.     divide numerator n by divisor d, rouding as closely as possible
  1999.     */
  2000.     
  2001.     if (n >= 0)
  2002.         return ((n + d / 2) / d);
  2003.     
  2004.     return ((n - d / 2) / d);
  2005.     } /*divRound*/
  2006.  
  2007.  
  2008. static Boolean opGetLineText (Handle htext, long ixlast, long *ix, short *level, Str255 bs) {
  2009.  
  2010.     /*
  2011.     extract a line of text from the handle at offset ix.  ix gets bumped to
  2012.     point at the beginning of the next line.
  2013.     
  2014.     the level is the number of ascii 9's at the head of the string.
  2015.     
  2016.     12/13/91 dmb: added support for space-indented text import
  2017.     */
  2018.     
  2019.     register long sizetext = ixlast;
  2020.     register long ixtext = *ix;
  2021.     register long ct = 0;
  2022.     register char *p;
  2023.     
  2024.     bs [0] = 0; /*in case we return early*/
  2025.     
  2026.     if (ixtext >= sizetext) /*no more characters available*/
  2027.         return (false);
  2028.         
  2029.     HLock (htext);
  2030.     
  2031.     p = *htext + ixtext;
  2032.     
  2033.     while (*p == (char) 9) { /*count the leading tab chars*/
  2034.     
  2035.         ct++;
  2036.         
  2037.         p++;
  2038.         
  2039.         ixtext++;
  2040.         
  2041.         if (ixtext >= sizetext) { /*no text on the line*/
  2042.             
  2043.             HUnlock (htext);
  2044.             
  2045.             return (false);
  2046.             }
  2047.         
  2048.         fltabindent = true;
  2049.         } /*while*/
  2050.     
  2051.     if (!fltabindent) {
  2052.         
  2053.         while (*p == ' ') { /*count the leading tab chars*/
  2054.             
  2055.             ct++;
  2056.             
  2057.             p++;
  2058.             
  2059.             ixtext++;
  2060.             
  2061.             if (ixtext >= sizetext) { /*no text on the line*/
  2062.                 
  2063.                 HUnlock (htext);
  2064.                 
  2065.                 return (false);
  2066.                 }
  2067.             } /*while*/
  2068.         
  2069.         if (ct > 0) {
  2070.             
  2071.             if (spacesforlevel == 0) /*first run of spaces*/
  2072.                 spacesforlevel = ct;
  2073.             
  2074.             ct = divRound (ct, spacesforlevel);
  2075.             }
  2076.         }
  2077.     
  2078.     if (firstindent < 0) /*first line of text*/
  2079.         firstindent = ct;
  2080.     
  2081.     *level = max (0, ct - firstindent);
  2082.     
  2083.     ct = 0;
  2084.     
  2085.     while (*p != (char) 13) { /*copy the text chars into the string*/
  2086.         
  2087.         if (ct >= 255) /*don't overwrite the buffer*/
  2088.             break;
  2089.             
  2090.         bs [0] = ++ct;
  2091.         
  2092.         bs [ct] = *p;
  2093.         
  2094.         p++;
  2095.         
  2096.         ixtext++;
  2097.         
  2098.         if (ixtext >= sizetext) /*ran out of characters in the buffer*/
  2099.             break;
  2100.         } /*while*/
  2101.     
  2102.     *ix = ixtext + 1;
  2103.     
  2104.     HUnlock (htext);
  2105.     
  2106.     return (true);
  2107.     } /*opGetLineText*/
  2108.  
  2109.  
  2110. pascal void opUnlink (hdlheadrecord hnode) {
  2111.     
  2112.     /*
  2113.     point all elements of the structure around the indicated node, and 
  2114.     set his "outbound" pointers (ie everything but his right pointer)  
  2115.     to point at himself.
  2116.     */
  2117.     
  2118.     register hdlheadrecord h = hnode;
  2119.     register hdlheadrecord hdown = (**h).headlinkdown;
  2120.     register hdlheadrecord hup = (**h).headlinkup;
  2121.     register hdlheadrecord hleft = (**h).headlinkleft;
  2122.     
  2123.     if (h == nil) /*defensive driving*/
  2124.         return;
  2125.  
  2126.     if (hup == h) { /*unlinking first guy at this level*/
  2127.  
  2128.         if (hdown == h) { /*only child*/
  2129.  
  2130.             (**hleft).headlinkright = hleft; /*eliminate parent's child list*/
  2131.             
  2132.             goto exit;
  2133.             }
  2134.  
  2135.         if (hleft != h) /*has a parent*/
  2136.             (**hleft).headlinkright = hdown;
  2137.         
  2138.         (**hdown).headlinkup = hdown; /*he's now the 1st kid*/
  2139.         
  2140.         goto exit;
  2141.         }
  2142.  
  2143.     /*has a sibling above*/
  2144.  
  2145.     if (hdown == h) { /*last guy in his list*/
  2146.     
  2147.         (**hup).headlinkdown = hup; /*he's now the last kid*/
  2148.  
  2149.         goto exit;
  2150.         }
  2151.  
  2152.     (**hup).headlinkdown = hdown; /*point around me*/
  2153.     
  2154.     (**hdown).headlinkup = hup;
  2155.     
  2156.     exit: /*goto here to exit*/
  2157.  
  2158.     (**h).headlinkup = (**h).headlinkdown = (**h).headlinkleft = h;
  2159.     } /*opUnlink*/
  2160.  
  2161.  
  2162. static opDepositDown (hdlheadrecord hpre, hdlheadrecord hdeposit) {
  2163.     
  2164.     /*
  2165.     deposit the indicated node down from hpre.
  2166.     
  2167.     modified 11/9/88 to support multiple level-0 nodes DW.
  2168.     
  2169.     11/10/88 handles moving the first summit down, updates 
  2170.     (**outlinedata).hsummit.
  2171.     
  2172.     12/23/88 more efficient code, use registers better.
  2173.     */
  2174.     
  2175.     register hdlheadrecord hp = hpre;
  2176.     register hdlheadrecord hd = hdeposit;
  2177.     register short headlevel = (**hp).headlevel;
  2178.     
  2179.     if (headlevel == 0)
  2180.         (**hd).headlinkleft = hd; /*points back at himself*/
  2181.     else
  2182.         (**hd).headlinkleft = (**hp).headlinkleft; /*inherits parent from pre*/
  2183.     
  2184.     (**hd).headlevel = headlevel; /*inherits level from pre*/
  2185.     
  2186.     (**hd).flexpanded = (**hp).flexpanded; /*must be consistent*/
  2187.     
  2188.     if ((**hp).headlinkdown == hp) /*inserting as the last in the list*/
  2189.     
  2190.         (**hd).headlinkdown = hd; /*point back at himself*/
  2191.         
  2192.     else { /*inserting in the middle of the list*/
  2193.         
  2194.         register hdlheadrecord x = (**hp).headlinkdown;
  2195.         
  2196.         (**hd).headlinkdown = x;
  2197.         
  2198.         (**x).headlinkup = hd;
  2199.         }
  2200.     
  2201.     (**hd).headlinkup = hp;
  2202.     
  2203.     (**hp).headlinkdown = hd;
  2204.  
  2205.     return; /*the following code could hide serious bugs, it's irrelevant here*/
  2206.     
  2207.     /*
  2208.     if (((**hp).headlinkup == hp) && (headlevel == 0)) /*make sure hsummit is correct%/
  2209.     
  2210.         (**outlinedata).hsummit = hp;
  2211.     */
  2212.     } /*opDepositDown*/
  2213.  
  2214.  
  2215. static opDepositRight (hdlheadrecord hparent, hdlheadrecord hdeposit) {
  2216.  
  2217.     register hdlheadrecord hp = hparent;
  2218.     register hdlheadrecord hd = hdeposit;
  2219.     register hdlheadrecord hr = (**hp).headlinkright;
  2220.     
  2221.     (**hp).fldirty = true;
  2222.     
  2223.     (**hd).headlevel = (**hp).headlevel + 1;
  2224.     
  2225.     (**hd).headlinkleft = hp;
  2226.     
  2227.     (**hd).headlinkup = hd; /*points at himself*/
  2228.     
  2229.     if (hr == hp) { /*first kid*/
  2230.         
  2231.         (**hd).headlinkdown = hd; /*points at himself*/
  2232.         
  2233.         (**hp).fldirty = true; /*might need to re-display the icon on the line*/
  2234.         }
  2235.     else {
  2236.         
  2237.         (**hd).headlinkdown = hr;
  2238.         
  2239.         (**hr).headlinkup = hd;
  2240.         
  2241.         (**hd).flexpanded = (**hr).flexpanded; /*must be consistent*/
  2242.         }
  2243.     
  2244.     (**hp).headlinkright = hd;
  2245.     } /*opDepositRight*/
  2246.  
  2247.  
  2248. static opDepositUp (hdlheadrecord hpre, hdlheadrecord hdeposit) {
  2249.     
  2250.     /*
  2251.     deposit the indicated node up from hpre.  special case considerations if
  2252.     we're moving at the very topmost level of the structure.
  2253.     */
  2254.     
  2255.     register hdlheadrecord hp = hpre;
  2256.     register hdlheadrecord hd = hdeposit;
  2257.     register hdlheadrecord hleft;
  2258.     register hdloutlinerecord ho = outlinedata;
  2259.             
  2260.     if (!opIsFirstInList (hp)) { /*simple in every case*/
  2261.     
  2262.         opDepositDown ((**hp).headlinkup, hd);
  2263.         
  2264.         return;
  2265.         }
  2266.     
  2267.     hleft = (**hp).headlinkleft;
  2268.     
  2269.     if (hleft != hp) { /*pre has a parent*/
  2270.     
  2271.         opDepositRight (hleft, hd); /*insert to right of pre's parent*/
  2272.         }
  2273.         
  2274.     else { /*insert as the new 0-level summit*/
  2275.         
  2276.         (**hd).headlevel = 0;
  2277.         
  2278.         (**hd).headlinkup = hd; 
  2279.         
  2280.         (**hd).headlinkdown = hp;
  2281.         
  2282.         (**hd).flexpanded = true; /*summits are always expanded*/
  2283.         
  2284.         (**hp).headlinkup = hd;
  2285.         
  2286.         if (hp == (**ho).hsummit) /*becomes the new summit*/
  2287.             (**ho).hsummit = hd;        
  2288.         }
  2289.         
  2290.     if (hp == (**ho).hline1) /*becomes the new first line*/
  2291.         (**ho).hline1 = hd;
  2292.     } /*opDepositUp*/
  2293.  
  2294.  
  2295. pascal Boolean opDeposit (hdlheadrecord hpre, opDirection dir, hdlheadrecord hdeposit) {
  2296.     
  2297.     switch (dir) {
  2298.     
  2299.         case opDown:
  2300.             opDepositDown (hpre, hdeposit);
  2301.             
  2302.             break;
  2303.             
  2304.         case opRight:
  2305.             opDepositRight (hpre, hdeposit);
  2306.             
  2307.             break;
  2308.             
  2309.         case opUp:
  2310.             opDepositUp (hpre, hdeposit);
  2311.             
  2312.             break;
  2313.             
  2314.         default:
  2315.             return (false); /*invalid direction for deposit*/
  2316.         } /*switch*/
  2317.     
  2318.     return (true);
  2319.     } /*opDeposit*/
  2320.     
  2321.     
  2322. pascal Boolean opPromoteSubheads (hdlheadrecord hnode) {
  2323.     
  2324.     while (true) {
  2325.         
  2326.         hdlheadrecord hsub = hnode;
  2327.         
  2328.         if (!opGo (&hsub, opRight)) /*has no subs*/
  2329.             return (true);
  2330.     
  2331.         while (opGo (&hsub, opDown)) {} /*move to the last sub*/
  2332.         
  2333.         opUnlink (hsub);
  2334.         
  2335.         opDepositDown (hnode, hsub);
  2336.         } /*while*/
  2337.     } /*opPromoteSubheads*/
  2338.  
  2339.  
  2340. pascal Boolean opAddHeadline (hdlheadrecord hpre, opDirection dir, Str255 bshead, hdlheadrecord *hnew) {
  2341.     
  2342.     /*
  2343.     create a new headrecord. it's position is relative to hpre, it's depositited in 
  2344.     the direction indicated by dir. return true if it worked, false if it didn't.
  2345.     */
  2346.     
  2347.     if (!opNewHeadRecord (bshead, hnew))
  2348.         return (false);
  2349.     
  2350.     return (opDeposit (hpre, dir, *hnew));
  2351.     } /*opAddHeadline*/
  2352.  
  2353.  
  2354. static Boolean textToOutline (Handle htext, long ixstart, long ixlast, hdlheadrecord *hnode) {
  2355.     
  2356.     register hdlheadrecord h = nil;
  2357.     register short lastlevel;
  2358.     register opDirection dir;
  2359.     hdlheadrecord hnewnode;
  2360.     long ixtext = ixstart;
  2361.     short level;
  2362.     Str255 bs;
  2363.     Boolean fl = false; /*guilty until proven innocent*/
  2364.     
  2365.     clearIndentValues (true);
  2366.     
  2367.     while (true) {
  2368.         
  2369.         if (!opGetLineText (htext, ixlast, &ixtext, &level, bs)) { /*consumed the text*/
  2370.             
  2371.             if (h != nil) /*success -- we got something*/
  2372.                 fl = true;
  2373.             
  2374.             break;
  2375.             }
  2376.         
  2377.         if (h == nil) { /*first line*/
  2378.             
  2379.             if (!opNewStructure (bs, hnode))
  2380.                 break;
  2381.             
  2382.             h = *hnode;
  2383.             
  2384.             lastlevel = 0;
  2385.             }
  2386.         else {
  2387.             if (level > lastlevel) 
  2388.                 dir = opRight;
  2389.         
  2390.             else {
  2391.                 h = opRepeatedBump (opLeft, lastlevel - level, h, true);
  2392.         
  2393.                 dir = opDown;
  2394.                 }
  2395.             
  2396.             if (!opAddHeadline (h, dir, bs, &hnewnode)) {
  2397.                 
  2398.                 opDisposeStructure (*hnode);
  2399.                 
  2400.                 break;
  2401.                 }
  2402.             
  2403.             h = hnewnode;
  2404.             
  2405.             lastlevel = level;
  2406.             }
  2407.         } /*while*/
  2408.     
  2409.     return (fl);
  2410.     } /*textToOutline*/
  2411.  
  2412.  
  2413. static Boolean unpackVersion2 (Handle hpackedoutline, long *ixload) {
  2414.     
  2415.     register hdloutlinerecord ho;
  2416.     register Handle h = hpackedoutline;
  2417.     register long ixstart;
  2418.     hdlheadrecord hsummit, hline1, hcursor;
  2419.     tydiskoutlineheader header;
  2420.     short fontnum;
  2421.     
  2422.     ho = outlinedata; /*copy into register*/
  2423.     
  2424.     if (!opLoadFromHandle (h, ixload, longsizeof (header), (char *) &header))
  2425.         return (false);
  2426.         
  2427.     (**ho).linespacing = header.linespacing;
  2428.     
  2429.     (**ho).lineindent = header.lineindent;
  2430.     
  2431.     (**ho).vertmin = header.vertmin;
  2432.     
  2433.     (**ho).vertmax = header.vertmax;
  2434.     
  2435.     (**ho).vertcurrent = header.vertcurrent;
  2436.     
  2437.     (**ho).horizmin = header.horizmin;
  2438.     
  2439.     (**ho).horizmax = header.horizmax;
  2440.     
  2441.     (**ho).horizcurrent = header.horizcurrent;
  2442.     
  2443.     (**ho).timecreated = header.timecreated;
  2444.     
  2445.     (**ho).timelastsave = header.timelastsave;
  2446.     
  2447.     (**ho).ctsaves = header.ctsaves;
  2448.     
  2449.     opDiskGetFontNum (header.fontname, &fontnum);
  2450.     
  2451.     (**ho).fontnum = fontnum;
  2452.     
  2453.     (**ho).fontsize = header.fontsize;
  2454.  
  2455.     (**ho).fontstyle = header.fontstyle;
  2456.     
  2457.     (**ho).windowrect = header.windowrect;
  2458.     
  2459.     ixstart = *ixload; /*first character in text*/
  2460.     
  2461.     if (!textToOutline (h, ixstart, ixstart + header.sizetext, &hsummit))
  2462.         return (false);
  2463.     
  2464.     *ixload += header.sizetext;
  2465.     
  2466.     opSetSummit (ho, hsummit);
  2467.     
  2468.     opSetExpandedBits (hsummit, true); /*all 1st level items are expanded*/
  2469.     
  2470.     if (!tableToOutline (h, ixstart + header.sizetext, hsummit))
  2471.         return (false);
  2472.     
  2473.     *ixload += header.sizelinetable;
  2474.     
  2475.     hline1 = opRepeatedBump (opFlatdown, header.vertcurrent, hsummit, true);
  2476.     
  2477.     (**ho).hline1 = hline1;
  2478.     
  2479.     hcursor = opRepeatedBump (opFlatdown, header.lnumcursor, hsummit, true);
  2480.     
  2481.     (**ho).hbarcursor = hcursor;
  2482.     
  2483.     opSetCountExpanded (); /*don't bother saving this on disk, we re-compute*/
  2484.     
  2485.     return (true);
  2486.     } /*unpackVersion2*/
  2487.     
  2488.     
  2489. pascal Boolean opUnpack (Handle hpackedoutline, long *ixload) {
  2490.     
  2491.     register Handle h = hpackedoutline;
  2492.     register long ixorig = *ixload;
  2493.     short versionnumber;
  2494.     hdloutlinerecord houtline;
  2495.     Boolean fl;
  2496.     
  2497.     if (!opNewOutlineRecord (&houtline))
  2498.         return (false);
  2499.     
  2500.     outlinedata = houtline;
  2501.     
  2502.     if (!opLoadFromHandle (h, ixload, longsizeof (versionnumber), (char *) &versionnumber))
  2503.         return (false);
  2504.     
  2505.     *ixload = ixorig; /*the header record includes the version number*/
  2506.     
  2507.     switch (versionnumber) {
  2508.         
  2509.         case 2:
  2510.             fl = unpackVersion2 (h, ixload);
  2511.             
  2512.             break;
  2513.         
  2514.         default:
  2515.             DebugStr ("\pbad outline version number");
  2516.             
  2517.             fl = false;
  2518.         } /*switch*/
  2519.     
  2520.     if (!fl)
  2521.         opDisposeOutlineRecord (houtline);
  2522.     
  2523.     return (fl);
  2524.     } /*opUnpack*/
  2525.  
  2526.  
  2527. pascal Boolean IACgetoutlineparam (keyword, houtline) OSType keyword; hdloutlinerecord *houtline; {
  2528.     
  2529.     /*
  2530.     get the outline parameter from the current Apple Event -- as indicated by the
  2531.     keyword. return true if it worked, false otherwise.
  2532.     
  2533.     follows the same pattern as the routines implemented in iac.c.
  2534.     */
  2535.     
  2536.     AEDesc result;
  2537.     OSErr ec;
  2538.     Boolean fl;
  2539.     long ix;
  2540.     
  2541.     ec = AEGetParamDesc (IACglobals.event, (AEKeyword) keyword, 'optx', &result);
  2542.     
  2543.     if (ec != noErr) {
  2544.         
  2545.         IACparamerror (ec, "\poutline", keyword);
  2546.         
  2547.         return (false);
  2548.         }
  2549.     
  2550.     opPushOutline (nil); /*preserve outlinedata*/
  2551.     
  2552.     ix = 4; /*start loading at offset 4*/
  2553.     
  2554.     fl = opUnpack (result.dataHandle, &ix);
  2555.     
  2556.     *houtline = outlinedata;
  2557.     
  2558.     opPopOutline ();
  2559.     
  2560.     AEDisposeDesc (&result);
  2561.     
  2562.     if (!fl)
  2563.         IACreturnerror (-1, "\pError unpacking outline parameter");
  2564.     
  2565.     return (fl);
  2566.     } /*IACgetoutlineparam*/
  2567.     
  2568.     
  2569. pascal Boolean IACpushoutlineparam (hdloutlinerecord val, OSType keyword) {
  2570.     
  2571.     /*
  2572.     push the outline on the current Apple Event with the indicated key.
  2573.     
  2574.     follows the same pattern as the routines implemented in iac.c.
  2575.     */
  2576.  
  2577.     AEDesc desc;
  2578.     register OSErr ec;
  2579.     register long len;
  2580.     Handle hpackedoutline;
  2581.     Boolean fl;
  2582.     long headerbytes;
  2583.     
  2584.     hpackedoutline = NewHandle (4);
  2585.     
  2586.     if (hpackedoutline == nil)
  2587.         return (false);
  2588.         
  2589.     headerbytes = 0x00010000; /*magic incantation to make Frontier happy*/
  2590.     
  2591.     BlockMove (&headerbytes, *hpackedoutline, 4);
  2592.     
  2593.     opPushOutline (val); /*preserve outlinedata*/
  2594.     
  2595.     fl = opPack (&hpackedoutline);
  2596.     
  2597.     opPopOutline ();
  2598.     
  2599.     if (!fl)
  2600.         return (false);
  2601.     
  2602.     desc.descriptorType = 'optx';
  2603.     
  2604.     desc.dataHandle = hpackedoutline;
  2605.     
  2606.     ec = AEPutParamDesc (IACglobals.event, (AEKeyword) keyword, &desc);
  2607.     
  2608.     AEDisposeDesc (&desc);
  2609.     
  2610.     return (ec == noErr);
  2611.     } /*IACpushoutlineparam*/
  2612.  
  2613.  
  2614. pascal Boolean IACreturnoutline (hdloutlinerecord houtline) {
  2615.     
  2616.     /*
  2617.     return the outline as the result of the current Apple Event.
  2618.         
  2619.     follows the same pattern as the routines implemented in iac.c.
  2620.     */
  2621.     
  2622.     register AppleEvent *oldevent = IACglobals.event;
  2623.     register Boolean fl = false;
  2624.     
  2625.     IACglobals.event = IACglobals.reply; /*push params on the reply record*/
  2626.     
  2627.     fl = IACpushoutlineparam (houtline, keyDirectObject);
  2628.     
  2629.     IACglobals.reply = oldevent; /*restore*/
  2630.     
  2631.     return (fl);
  2632.     } /*IACreturnoutline*/
  2633.     
  2634.     
  2635.